package controller;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Observable;
import java.util.Observer;

import model.information.*;
import model.userhandler.*;
import view.*;



/**
 * Controller class, executes the program and handles the communication between Model and View.
 * @author Tobias Møller Kjærsgaard
 * @author Hjalte Blichfeldt Flyger
 * @author Gitte Simonsen
 * @author Claus Lund
 */
public class Controller implements Observer {
    private UserManager uManager;
    private User user;
    private InformationManager iManager;
    private Frame frame;
    private LoginPanel loginPanel;
    private ManagerPanel managerPanel;
    private StartPanel startPanel;
    private ContactPanel contactPanel;
    private EventPanel eventPanel;
    private JournalPanel journalPanel;
    private LinkPanel linkPanel;
    private MemoPanel memoPanel;
    private PasswordPanel passPanel;
    private ResearchPanel researchPanel;

    /**
     * Saves data to the harddisk on application shutdown.
     */
    public class ShutdownThread extends Thread {
        private Controller ctrl;

        /**
         * A contructor that sets the ctrl field.
         * @param ctrl a Controller to set as ctrl.
         */
        public ShutdownThread(Controller ctrl) {
            this.ctrl = ctrl;
        }

        /**
         * Saves the UserManager to a file.
         */
        @Override
        public void run() {
            ctrl.saveUManager();
        }
    }

    /**
     * A constructor that creates the GUI
     */
    public Controller() {
        frame = new Frame();
        uManager = openUManager();
        loginPanel = new LoginPanel(this, uManager);
        frame.setContentPane(loginPanel);
        frame.pack();
        frame.setVisible(true);
        Runtime.getRuntime().addShutdownHook(new ShutdownThread(this));
    }

    /**
     * Creates the main elements of the GUI immediately after user login.
     * @param user the user object to serve.
     */
    public void logon(User user) {
        this.user = user;
        this.iManager = user.getIManager();
        iManager.addObserver(this);

        startPanel = new StartPanel(this);
        contactPanel = new ContactPanel(this);
        eventPanel = new EventPanel(this);
        journalPanel = new JournalPanel(this);
        linkPanel = new LinkPanel(this);
        memoPanel = new MemoPanel(this);
        passPanel = new PasswordPanel(this);
        researchPanel = new ResearchPanel(this);
        managerPanel = new ManagerPanel(this,
                startPanel,
                contactPanel,
                eventPanel,
                journalPanel,
                linkPanel,
                memoPanel,
                passPanel,
                researchPanel);

        managerPanel.addMenuBar();
        frame.setContentPane(managerPanel);
        frame.pack();
    }

    /**
     * Creates a new user, and adds it to the UserManager.
     * @param username desired username.
     * @param password desired password.
     * @param firstName the first name of the user.
     * @param lastName the last name of the user.
     * @return
     */
    public User createUser(String username, String password, String firstName, String lastName) {
        InformationManager newIManager = new InformationManager();
        User newUser = new User(username, password, firstName, lastName, newIManager);
        uManager.add(newUser);
        return newUser;
    }

    /**
     * Removes a user from the UserManager.
     * @param o the user to remove.
     */
    public void removeUser(User o) {
        uManager.remove(o);
    }

    /**
     * Open a UserManager from a specific file in the current working directory, or create a new one if import fails.
     * @return a UserManager.
     */
    public UserManager openUManager() {
        File file = new File("users.lmu");
        FileInputStream fIn = null;
        ObjectInputStream oIn = null;

        try {
            fIn = new FileInputStream(file);
            oIn = new ObjectInputStream(fIn);
            uManager = (UserManager)oIn.readObject();
            oIn.close();
            fIn.close();
            System.out.println("Loaded users from file: " + file.getAbsolutePath());
        } catch (IOException ex) {
            //ex.printStackTrace();
            //frame.errorMessage("Brugerne kunne ikke indlæses fra filen, opret en ny bruger i login-vinduet.", "I/O Fejl");
            System.out.println("Could not load users from a file, creating a new UserManager.");
            uManager = new UserManager();
        } catch (ClassNotFoundException ex) {
            //ex.printStackTrace();
            frame.errorMessage("Brugerne kunne ikke indlæses fra filen, opret en ny bruger i login-vinduet.", "Casting Fejl");
            System.out.println("Could not load users from a file, creating a new UserManager.");
            uManager = new UserManager();
        } catch (Exception ex) {
            //ex.printStackTrace();
            frame.errorMessage("Brugerne kunne ikke indlæses fra filen, opret en ny bruger i login-vinduet.", "Uventet Fejl");
            System.out.println("Could not load users from a file, creating a new UserManager.");
            uManager = new UserManager();
        }
        return uManager;
    }

    /**
     * Save the current UserManager to a file in the applications current working directory.
     */
    public boolean saveUManager() {
        File file = new File("users.lmu");
        FileOutputStream fOut = null;
        ObjectOutputStream oOut = null;

        try {
            fOut = new FileOutputStream(file);
            oOut = new ObjectOutputStream(fOut);
            oOut.writeObject(uManager);
            oOut.close();
            fOut.close();
            System.out.println("Saved users to file: " + file.getAbsolutePath());
            return true;
        } catch (IOException ex) {
            //ex.printStackTrace();
            frame.errorMessage("Der opstod problemer under skrivning af data til harddisken.", "I/O Fejl");
            return false;
        }
    }

    /**
     * Removes all events with start dates earlier than today.
     * @return the number of events removed.
     */
    public int cleanupEvents() {
        Calendar today = Calendar.getInstance();
        today.set(Calendar.HOUR_OF_DAY, 00);
        today.set(Calendar.MINUTE, 00);
        today.set(Calendar.SECOND, 00);
        return iManager.removeOldEvents(today);
    }

    /**
     * Removes all memos dated earlier than today.
     * @return the number of memos removed.
     */
    public int cleanupMemos() {
        Calendar today = Calendar.getInstance();
        today.set(Calendar.HOUR_OF_DAY, 00);
        today.set(Calendar.MINUTE, 00);
        today.set(Calendar.SECOND, 00);
        return iManager.removeOldMemos(today);
    }

    /**
     * Sort events by date.
     */
    public void sortEvents() {
        iManager.sortEvents();
    }

    /**
     * Sort contacts by first name.
     */
    public void sortContacts() {
        iManager.sortContacts();
    }

    /**
     * Get the current User object.
     * @return the current User.
     */
    public User getUsr() {
        return user;
    }

    /**
     * Get the current InformationManager
     * @return the current InformationManager.
     */
    public InformationManager getIManager() {
        return iManager;
    }

    /**
     * Get the current Swing GUI frame.
     * @return a Swing Frame.
     */
    public Frame getFrame() {
        return frame;
    }

    /**
     * Get the current UserManager.
     * @return the current UserManager.
     */
    public UserManager getUManager() {
        return uManager;
    }

    /**
     * Remove a Research object from the iManager.
     * @param o the Research object to reove.
     */
    public void removeResearch(Research o) {
        iManager.removeResearch(o);
    }

    /**
     * Remove a Password object from the iManager.
     * @param o the Password object to remove.
     */
    public void removePassword(Password o) {
        iManager.removePassword(o);
    }

    /**
     * Remove a Memo object from the iManager.
     * @param o the Memo object to remove.
     */
    public void removeMemo(Memo o) {
        iManager.removeMemo(o);
    }

    /**
     * Remove a Link object from the iManager.
     * @param o the Link object to remove.
     */
    public void removeLink(Link o) {
        iManager.removeLink(o);
    }

    /**
     * Remove a JournalEntry object from the iManager.
     * @param o the JournalEntry object to remove.
     */
    public void removeJournalEntry(JournalEntry o) {
        iManager.removeJournalEntry(o);
    }

    /**
     * Remove an Event object from the iManager.
     * @param o the Event object to remove.
     */
    public void removeEvent(Event o) {
        iManager.removeEvent(o);
    }

    /**
     * Remove a Contact object from the iManager.
     * @param o the Contact object to remove.
     */
    public void removeContact(Contact o) {
        iManager.removeContact(o);
    }

    /**
     * Remove a Meal object from the iManager.
     * @param o the Meal object to remove.
     */
    public void removeMeal(Meal o) {
        iManager.removeMeal(o);
    }

    /**
     * Get the ResearchNotes in the iManager.
     * @return an ArrayList of ResearchNote objects in the iManager.
     */
    public ArrayList<Research> getResearchnotes() {
        return iManager.getResearchnotes();
    }

    /**
     * Get the Passwords in the iManager.
     * @return an ArrayList of Password objects in the iManager.
     */
    public ArrayList<Password> getPasswords() {
        return iManager.getPasswords();
    }

    /**
     * Get the Memos in the iManager.
     * @return an ArrayList of Memo object in the iManager.
     */
    public ArrayList<Memo> getMemos() {
        return iManager.getMemos();
    }

    /**
     * Get the Links in the iManager.
     * @return an ArrayList of Link objects in the iManager.
     */
    public ArrayList<Link> getLinks() {
        return iManager.getLinks();
    }

    /**
     * Get the JournalEntries from in the iManager.
     * @return an ArrayList of JournalEntry objects in the iManager.
     */
    public ArrayList<JournalEntry> getJournal() {
        return iManager.getJournal();
    }

    /**
     * Get the Events in the iManager.
     * @return an ArrayList of Event objects in the iManager.
     */
    public ArrayList<Event> getEvents() {
        return iManager.getEvents();
    }

    /**
     * Get the Contacts in the iManager.
     * @return an ArrayList of Contact objects in the iManager.
     */
    public ArrayList<Contact> getContacts() {
        return iManager.getContacts();
    }

    /**
     * Get the Meals in the iManager.
     * @return an ArrayList of Meail objects in the iManager.
     */
    public ArrayList<Meal> getMeals() {
        return iManager.getMeals();
    }

    /**
     * Creates a new researchnote from the information given, and adds it to the iManager.
     * @param title the title.
     * @param text the text.
     * @return the new researchnote.
     */
    public Research addResearch(String title, String text) {
        Research r = new Research(title, text);
        iManager.addResearch(r);
        r.addObserver(this);
        return r;
    }

    /**
     * Creates a new password from the information given and adds it to the iManager.
     * @param title the title.
     * @param username the username.
     * @param password the password.
     * @return the new password.
     */
    public Password addPassword(String title, String username, String password) {
        Password p = new Password(title, username, password);
        iManager.addPassword(p);
        p.addObserver(this);
        return p;
    }

    /**
     * Creates a new memo from the information given and adds it to the iManager.
     * @param note the text.
     * @return the new memo.
     */
    public Memo addMemo(String note) {
        Memo m = new Memo(note);
        iManager.addMemo(m);
        m.addObserver(this);
        return m;
    }

    /**
     * Creates a new link from the information given and adds it to the iManager.
     * @param title the title.
     * @param url the url.
     * @return the new link.
     */
    public Link addLink(String title, String url) {
        Link l = new Link(title, url);
        iManager.addLink(l);
        l.addObserver(this);
        return l;
    }

    /**
     * Creates a new journal entry from the information given and adds it to the iManager.
     * @param title the title.
     * @param text the text.
     * @return the new journal entry.
     */
    public JournalEntry addJournalEntry(String title, String text) {
        JournalEntry j = new JournalEntry(title, text);
        iManager.addJournalEntry(j);
        j.addObserver(this);
        return j;
    }

    /**
     * Creates a new event from the information given and adds it to the iManager.
     * @param title the title.
     * @param location the location.
     * @param note a note to.
     * @return the new event.
     */
    public Event addEvent(String title, String location, String note) {
        Event e = new Event(title, location, note);
        iManager.addEvent(e);
        e.addObserver(this);
        return e;
    }

    /**
     * Creates a new contact from the information given and adds it to the iManager.
     * @param fn the firstname.
     * @param ln the lastname.
     * @return the new contact.
     */
    public Contact addContact(String fn, String ln) {
        Contact c = new Contact(fn, ln);
        iManager.addContact(c);
        c.addObserver(this);
        return c;
    }

    /**
     * Creates a new contact from the information given and adds it to the iManager.
     * @param firstName the first name.
     * @param lastName the last name.
     * @param adress the address.
     * @param postalCode the postal code.
     * @param city the city.
     * @param country the country.
     * @param email the e-mail address.
     * @return the new contact.
     */
    public Contact addContact(String firstName, String lastName, String adress, String postalCode, String city, String country, String email) {
        Contact c = new Contact(firstName, lastName, adress, postalCode, city, country, email);
        iManager.addContact(c);
        c.addObserver(this);
        return c;
    }

    /**
     * Creates an new Meal from the information given and adds it to the iManager.
     * @param name the name.
     * @return the new meal.
     */
    public Meal addMeal(String name) {
        Meal m = new Meal(name);
        iManager.addMeal(m);
        return m;
    }

    /**
     * This method is run when the Controller recieves a notification from an object that the Controller is observing.
     * @param o the observable object that sent the notification.
     * @param arg argument sent with the notification.
     */
    public void update(Observable o, Object arg) {
        System.out.println("Recieved notification from: " + o + ", containing: " + arg);

        if(o.equals(iManager) && arg.equals("contacts")) {
            contactPanel.updateList();
        }

        if(o.getClass().equals(Contact.class)) {
            contactPanel.updateFromSave(o);
        }

        if(o.equals(iManager) && arg.equals("journal")) {
            journalPanel.updateList();
        }

        if(o.getClass().equals(JournalEntry.class)) {
            journalPanel.updateFromSave(o);
        }
        
        if(o.equals(iManager) && arg.equals("events")) {
            eventPanel.updateList();
            startPanel.updateStartEvents();
        }

        if(o.getClass().equals(Event.class)) {
            eventPanel.updateFromSave(o);
            startPanel.updateStartEvents();
        }

        if(o.equals(iManager) && arg.equals("links")) {
            linkPanel.updateList();
        }

        if(o.getClass().equals(Link.class)) {
            linkPanel.updateFromSave(o);
        }

        if(o.equals(iManager) && arg.equals("memos")) {
            memoPanel.updateList();
        }

        if(o.getClass().equals(Memo.class)) {
            memoPanel.updateFromSave(o);
        }

        if(o.equals(iManager) && arg.equals("passwords")) {
            passPanel.updateList();
        }

        if(o.getClass().equals(Password.class)) {
            passPanel.updateFromSave(o);
        }

        if(o.equals(iManager) && arg.equals("researchnotes")) {
            researchPanel.updateList();
        }

        if(o.getClass().equals(Research.class)) {
            researchPanel.updateFromSave(o);
        }
    }

}
